﻿using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Linq;
using System.ServiceProcess;
using System.Text;
using System.IO.Ports;
using System.Configuration;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Timers;

namespace EnergyService_CSharp
{
    public partial class EnergyService : ServiceBase
    {
        SerialPort comm;
        int baudRate;
        string portName;
        int dataBits;
        Parity parity;
        StopBits stopBits;
        string rxBuffer;
        CompositionContainer container;
        Timer tmr;
        string debugReading;


        public EnergyService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            try
            {
                plugins = new List<IEnergyMonitorPlugin>();
                LoadPlugins();
                InitializePlugins();

                if (bool.Parse(GetSetting("debug_enabled", "false")))
                {
                    debugReading = GetSetting("debug_reading", @"<msg><src>CC128-v0.11</src><dsb>00089</dsb><time>13:02:39</time><tmpr>18.7</tmpr><sensor>1</sensor><id>01234</id><type>1</type><ch1><watts>00345</watts></ch1><ch2><watts>02151</watts></ch2><ch3><watts>00000</watts></ch3></msg>");
                    tmr = new Timer(6000);
                    tmr.Elapsed += new ElapsedEventHandler(tmr_Elapsed);
                    tmr.Start();
                }
                else
                {
                    try
                    {
                        ConfigureCommPort();
                        comm = new SerialPort(portName, baudRate, parity, dataBits, stopBits);
                        comm.DataReceived += new SerialDataReceivedEventHandler(SerialDataReceived);
                        comm.Open();
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine("Error (OnStart) : " + ex.Message);
                    }
                }

            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine("Error (OnStart) : " + ex.Message);
            }

        }
        protected override void OnPause()
        {
            OnStop();
        }

        protected override void OnContinue()
        {
            OnStart(null);
        }

        protected override void OnStop()
        {
            // clean everything up
            DeInitializePlugins();
            plugins.Clear();
            plugins = null;
            container.Dispose();
            container = null;
            if (null != comm)
            {
                comm.Close();
                comm.Dispose();
            }

            // tidy up the debug stuff if it is present
            if (null != tmr)
            {
                tmr.Stop();
                tmr.Dispose();
            }
        }

        private void LoadPlugins()
        {
            DirectoryCatalog currDirCatalog = new DirectoryCatalog(AppDomain.CurrentDomain.BaseDirectory);
            container = new CompositionContainer(currDirCatalog);
            container.ComposeParts(this);
        }

        private void InitializePlugins()
        {
            if (null != plugins)
            {
                foreach (IEnergyMonitorPlugin plugin in plugins)
                {
                    try
                    {
                        if (plugin.Enabled)
                        {
                            plugin.Init();
                        }
                    }
                    catch(Exception ex)
                    {
                        Debug.WriteLine("Error Initializing (Plugin '" + plugin.Name + "') : " + ex.Message);
                    }
                }
            }
        }

        private void DeInitializePlugins()
        {
            if (null != plugins)
            {
                foreach (IEnergyMonitorPlugin plugin in plugins)
                {
                    try
                    {
                        if (plugin.Enabled)
                        {
                            plugin.DeInit();
                        }
                    }
                    catch (Exception ex)
                    {
                        Debug.WriteLine("Error DeInitializing (Plugin '" + plugin.Name + "') : " + ex.Message);
                    }
                }
            }
        }

        private void NotifyPlugins(EnergyReading rdg)
        {
            Debug.WriteLine("Notify : " + rdg.ToString());
            if (null != plugins)
            {
                foreach (IEnergyMonitorPlugin plugin in plugins)
                {
                    try
                    {
                        if (plugin.Enabled)
                        {
                            Debug.WriteLine("Notify : " + plugin.Name);
                            plugin.Notify(rdg);
                        }
                    }
                    catch(Exception ex)
                    {
                        Debug.WriteLine("Error Notifying (Plugin '" + plugin.Name + "') : " + ex.Message);
                    }
                }
            }
        }

        private EnergyReading ExtractReading(ref string rxBuffer)
        {
            string rdgXml;
            int pos = rxBuffer.IndexOf(Environment.NewLine);
            rdgXml = rxBuffer.Substring(0, pos);
            rxBuffer = rxBuffer.Substring(pos + 2);
            return new EnergyReading(rdgXml);
        }

        private bool IsReading(string rxBuffer)
        {
            // if it has a newline char then it is the end of a reading
            return (rxBuffer.IndexOf(Environment.NewLine) != -1);
        }

        private void ConfigureCommPort()
        {
            portName = GetSetting("comm_port", "COM3");
            baudRate = 57600;
            dataBits = 8;
            parity = Parity.None;
            stopBits = StopBits.One;

        }

        void SerialDataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            rxBuffer += comm.ReadExisting();
            while (IsReading(rxBuffer))
            {
                // we have a reading (or partial reading), so let's process
                EnergyReading rdg = ExtractReading(ref rxBuffer);
                if (rdg.IsValid)
                {
                    NotifyPlugins(rdg);
                }
            }
        }



        [ImportMany]
        private IList<IEnergyMonitorPlugin> plugins { get; set; }


        void tmr_Elapsed(object sender, ElapsedEventArgs e)
        {
            EnergyReading rdg = new EnergyReading(debugReading);
            NotifyPlugins(rdg);
        }

        private string GetSetting(string key, string defaultValue)
        {
            NameValueCollection appSettings = ConfigurationManager.AppSettings;
            string val = defaultValue;
            try
            {
                string v = appSettings.Get(key);
                if (null != v)
                {
                    val = v;
                }
            }
            catch
            {
                // just ignore
            }
            return val;
        }


    }
}
